package utils;

import java.util.Arrays;
import java.util.Hashtable;
import java.util.Map;

public class ByteStack {

	public static final int MAX_LENGTH = 1024;
	
	// map of byte arrays, every byte array has a size of MAX_LENGTH
	private Map<Integer, byte[]> map_byteArray = new Hashtable<Integer, byte[]>();
	
	private int current_index = -1;
	
	
	// internal address, will turn the current index to these two int
	// listPos refer to the index of element in the list
	// arrayPOs refer to the index of array element in the array
	private int nextListPos=0;
	private int nextArrayPos=0;
	
	
	public synchronized void write(byte[] b, int start, int size){
		for(int i=0;i<size;i++){
			write(b[start+i]);
		}
	}
	
	public synchronized void write(byte b){
		// calc the right position of the next position
		calcNextInternalPosition();
		
		// stuff the byte
		stuffByte(b);
	}
	
	public synchronized void write(byte[] b){
		write(b,0,b.length);
	}
	
	
	private void calcNextInternalPosition(){
		int next_index = current_index + 1;
		nextListPos = next_index / MAX_LENGTH;
		nextArrayPos = next_index % MAX_LENGTH;
		
		// prepare the byte array
		byte[] b = map_byteArray.get(nextListPos);
		if(b == null){
			b = new byte[MAX_LENGTH];
			map_byteArray.put(nextListPos, b);
		}
	}
	
	private void stuffByte(byte b){
		map_byteArray.get(nextListPos)[nextArrayPos] = b;
		current_index++;
	}
	
	public synchronized void manifest(){
		System.out.print("Map size: ");
		System.out.println(map_byteArray.size());
		System.out.print("Size: ");
		System.out.println(size());
		System.out.println("----------------------------------------");
		
		for(byte[] bb : map_byteArray.values()){
			for(byte b : bb){
				System.out.print(b);
				System.out.print(" ");
			}
			System.out.println();
		}
		
	}
	
	
	public synchronized byte getByte(int index){
		if(index > current_index) {
			throw new RuntimeException("Index " + index + " out of range "+current_index);
		}
		int listP = index / MAX_LENGTH;
		int arrayP = index % MAX_LENGTH;
		byte b = map_byteArray.get(listP)[arrayP];
		return b;
	}
	
	public synchronized byte[] getByteArray(){
		byte[] b = new byte[size()];
		
		for(int i=0;i<size();i++){
			b[i] = getByte(i);
		}
		return b;
	}
	
	
	public synchronized byte[] getByteArray(int start, int size){
		if(start+size>this.size()){
			throw new RuntimeException("Index " + (start + size) + " out of range "+this.size());
		}
		
		byte[] b = new byte[size];
		for(int i=0;i<size;i++){
			b[i] = getByte(start+i);
		}
		
		return b;
	}
	
	/**
	 * like what bytearrayoutputstream did
	 */
	public synchronized void reset(){
		current_index = -1;
	}
	
	public synchronized int size(){
		return current_index+1;
	}
	
	public static void main(String[] args) {
		byte[] b = new byte[]{1,2,3,4,5,6,7,8,9,10};
		ByteStack bs = new ByteStack();
		
		bs.write(b, 0, b.length);
		
		bs.manifest();
		
		System.out.println(Arrays.toString(bs.getByteArray()));
		
		System.out.println(Arrays.toString(bs.getByteArray(1, 3)));
		
		bs.reset();
		
		bs.write("hello, world!".getBytes());
		bs.manifest();
		System.out.println(new String(bs.getByteArray()));
		
	}
	
}



